{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 如何向Agent添加记忆"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们向智能体（Agent）添加了记忆组件。具体步骤如下：\n",
    "\n",
    "首先，我们导入了所需的模块和类，包括 `ZeroShotAgent`、`Tool`、`AgentExecutor`、`ConversationBufferMemory`、`ChatOpenAI`、`LLMChain` 。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.agents import ZeroShotAgent, Tool, AgentExecutor\n",
    "from langchain.memory import ConversationBufferMemory"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "使用 OpenAI，需要设置 openai_api_key 密钥，最好是使用 Chat Model 类的模型包装器 ChatOpenAI。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\freestone\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python311\\site-packages\\deeplake\\util\\check_latest_version.py:32: UserWarning: A newer version of deeplake (3.6.19) is available. It's recommended that you update to the latest version using `pip install -U deeplake`.\n",
      "  warnings.warn(\n"
     ]
    }
   ],
   "source": [
    "from langchain.chat_models import ChatOpenAI\n",
    "openai_api_key=\"填入你的OPENAI_API_KEY密钥\"\n",
    "llm = ChatOpenAI(openai_api_key=openai_api_key,temperature=0, model=\"gpt-3.5-turbo-0613\")\n",
    "from langchain.chains import LLMChain"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "使用 Google 搜索，需要设置 SERPAPI_API_KEY 密钥。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.environ[\"SERPAPI_API_KEY\"] = \"填入你的SERPAPI_API_KEY密钥\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "设置代理类型为 ZERO_SHOT_REACT_DESCRIPTION，加载所有的 tools，初始化这个代理。运行代理，我们询问“请告诉我OPENAI的CEO是谁？”。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'OPENAI的CEO是Sam Altman。'"
      ]
     },
     "execution_count": 105,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.agents import initialize_agent, load_tools\n",
    "from langchain.agents import AgentType\n",
    "\n",
    "tools = load_tools([\"serpapi\", \"llm-math\"], llm=llm)\n",
    "agent = initialize_agent(\n",
    "    tools,\n",
    "    llm,\n",
    "    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,\n",
    "\n",
    ")\n",
    "agent.run(\"请告诉我OPENAI的CEO是谁？\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在开始加入记忆组件。`Tools` 对象将作为智能体的一个工具，用于回答有关当前事件的问题可以借助的工具。我们指定了 `LLMChain` 作为链，并设置了 `tools` 作为工具。\n",
    "\n",
    "接着，我们创建了一个 `LLMChain` 实例，并使用它来初始化 `ZeroShotAgent`。在 `LLMChain` 的初始化中，我们指定了 `ChatOpenAI` 作为大语言模型，并设置了 `prompt` 作为提示词模板。在 `ZeroShotAgent` 的初始化中，我们指定了 `LLMChain` 作为链，并设置了 `tools` 作为工具。\n",
    "\n",
    "然后，我们将 `ZeroShotAgent` 和 `tools` 一起传入 `AgentExecutor.from_agent_and_tools` 方法，创建了一个 `AgentExecutor` 实例。在这个过程中，我们还指定了 `memory` 作为记忆组件。\n",
    "\n",
    "最后，我们使用 `AgentExecutor` 实例的 `run` 方法，运行智能体，并向其提出问题 \"上海的人口是多少？\"。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mThought: 我需要搜索一下上海的人口数据。\n",
      "Action: Search\n",
      "Action Input: \"上海人口数据\"\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m26.32 million (2019)\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m我现在知道了上海的人口是2632万（2019年）。\n",
      "Final Answer: 上海的人口是2632万（2019年）。\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'上海的人口是2632万（2019年）。'"
      ]
     },
     "execution_count": 112,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prefix = \"\"\"你是一个说中文的chatbot,你可以使用tools帮你获得答案:\"\"\"\n",
    "suffix = \"\"\"你的中文回答是：\"\n",
    "\n",
    "聊天记录：{chat_history}\n",
    "Question: {input}\n",
    "{agent_scratchpad}\"\"\"\n",
    "\n",
    "prompt = ZeroShotAgent.create_prompt(\n",
    "    tools,\n",
    "    prefix=prefix,\n",
    "    suffix=suffix,\n",
    "    input_variables=[\"input\", \"chat_history\", \"agent_scratchpad\"],\n",
    ")\n",
    "memory = ConversationBufferMemory(memory_key=\"chat_history\")\n",
    "\n",
    "llm_chain = LLMChain(llm=llm, prompt=prompt)\n",
    "agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools, verbose=True)\n",
    "agent_chain = AgentExecutor.from_agent_and_tools(\n",
    "    agent=agent, tools=tools, verbose=True, memory=memory\n",
    ")\n",
    "\n",
    "agent_chain.run(\"上海的人口是多少？\")\n",
    "\n",
    "\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了测试加入记忆组件的 Agent 是否更加智能，第二个问题，使用代词“它”来迷惑大语言模型，就像我们跟朋友聊天时，我们谈论一个人，可能只会提及一次姓名。后面的聊天都会使用人称代词或者缩写，而不用每次都使用全名。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mThought: 我需要搜索上海的地标建筑。\n",
      "Action: Search\n",
      "Action Input: 上海地标建筑\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3mhttps://cn.tripadvisor.com/Attractions-g308272-Activities-c47-Shanghai.html\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3m我现在知道上海的地标建筑了。\n",
      "Final Answer: 上海的地标建筑包括东方明珠广播电视塔、外滩、上海博物馆等。\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'上海的地标建筑包括东方明珠广播电视塔、外滩、上海博物馆等。'"
      ]
     },
     "execution_count": 113,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_chain.run(\"它的地标建筑是什么？\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以看到，虽然代码的大部分步骤都和之前一样，但是在创建 `AgentExecutor` 的时候，我们并没有指定 `memory` 参数。\n",
    "\n",
    "为了对比，记忆组件在Agent组件中，发挥的什么作用？我们再创建一个Agent组件，但是不要加入记忆组件。我们使用相同的问题，测试Agent是否知道第二个问题“它的地标建筑是什么”，“它”指代的是“上海”。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mThought: I need to find the current population of Shanghai.\n",
      "Action: Search\n",
      "Action Input: \"上海的人口是多少？\"\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3m26.32 million (2019)\u001b[0m\n",
      "Thought:"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 1.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-0WG0lwoFORBdfkEYprKFZ7FW on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n",
      "Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 2.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-0WG0lwoFORBdfkEYprKFZ7FW on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n",
      "Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-0WG0lwoFORBdfkEYprKFZ7FW on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n",
      "Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 8.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-0WG0lwoFORBdfkEYprKFZ7FW on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32;1m\u001b[1;3mI now know the current population of Shanghai.\n",
      "Final Answer: The population of Shanghai is 26.32 million (2019).\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'The population of Shanghai is 26.32 million (2019).'"
      ]
     },
     "execution_count": 109,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prefix = \"\"\"你是一个说中文的chatbot,你可以使用tools帮你获得答案:\"\"\"\n",
    "suffix = \"\"\"Begin!\"\n",
    "\n",
    "\n",
    "Question: {input}\n",
    "{agent_scratchpad}\"\"\"\n",
    "\n",
    "prompt = ZeroShotAgent.create_prompt(\n",
    "    tools,\n",
    "    prefix=prefix,\n",
    "    suffix=suffix,\n",
    "    input_variables=[\"input\", \"agent_scratchpad\"],\n",
    ")\n",
    "memory = ConversationBufferMemory(memory_key=\"chat_history\")\n",
    "\n",
    "llm_chain = LLMChain(llm=llm, prompt=prompt)\n",
    "agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools, verbose=True)\n",
    "agent_chain = AgentExecutor.from_agent_and_tools(\n",
    "    agent=agent, tools=tools, verbose=True\n",
    ")\n",
    "\n",
    "agent_chain.run(\"上海的人口是多少？\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从给出的答案“ 'The landmark building of \"it\" is the Empire State Building in New York City.' ”,我们可以对比得出，没有记忆组件的 Agent,面对“它”这样的指示代词无能无力，给出了莫名其妙的一个答案。它并不能关联上下文，推理出“它”指的是“上海”。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mThought: I need to find out the landmark building of \"it\".\n",
      "Action: Search\n",
      "Action Input: \"it landmark building\"\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3mLandmark Builds, Joplin, Missouri. 186 likes · 1 talking about this. Engaging teens in local history and STEM education by tapping the creative power of ...\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3mThis search result is not relevant to the question. I need to refine my search query.\n",
      "Action: Search\n",
      "Action Input: \"it city landmark building\"\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3mLandmark buildings are icons of a place. They create a statement about the city's legacy and influence how we think of that place. Landmark buildings stand out ...\u001b[0m\n",
      "Thought:\u001b[32;1m\u001b[1;3mThis search result is still not relevant to the question. I need to refine my search query further.\n",
      "Action: Search\n",
      "Action Input: \"it city famous landmark building\"\u001b[0m\n",
      "Observation: \u001b[36;1m\u001b[1;3mEmpire State Building, Manhattan, New York City, USA, the Art Deco skyscraper is New York's most popular landmark and a symbol for the American way of life, ...\u001b[0m\n",
      "Thought:"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 1.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-0WG0lwoFORBdfkEYprKFZ7FW on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n",
      "Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 2.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-0WG0lwoFORBdfkEYprKFZ7FW on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n",
      "Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-0WG0lwoFORBdfkEYprKFZ7FW on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[32;1m\u001b[1;3mThis search result is relevant to the question. The landmark building of \"it\" is the Empire State Building in New York City.\n",
      "Thought: I now know the final answer.\n",
      "Final Answer: The landmark building of \"it\" is the Empire State Building in New York City.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'The landmark building of \"it\" is the Empire State Building in New York City.'"
      ]
     },
     "execution_count": 110,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_chain.run(\"它的地标建筑是什么？\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "总的来说，这段代码展示了如何向 Agent 添加记忆组件，以及记忆组件在 Agent 中的作用。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.7"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
